#include "Network.h"

#include "NetworkEvents.h"

//TODO: This is kind of bad
#include <windows.h>

Multiplayer::Multiplayer()
{
    
    m_bIsServer = false;
    
    m_pServer = NULL;
    m_pClient = NULL;
    
}

void Multiplayer::host(unsigned short iMaxPlayers, unsigned short iPort)
{
    
    m_bIsServer = true;
    
    m_pServer = RakNetworkFactory::GetRakServerInterface();
    m_pServer->Start(iMaxPlayers, 0, 30, iPort);
    m_pServer->StartOccasionalPing();
    
    //TODO: Consider allowing server acting as client by calling join to self here
    //join
    
}

void Multiplayer::join(const char* cpHostIP, unsigned short iServerPort,
                                                     unsigned short iClientPort)
{
    
    m_bIsServer = false;
    
    m_pClient = RakNetworkFactory::GetRakClientInterface();
    m_pClient->Connect(cpHostIP, iServerPort, iClientPort, 0, 30);

    //printf("Connecting...\n");
    
    //TODO: Add timeout checking
    
    while(!m_pClient->IsConnected()) {
		//TODO: I think this is a Windows-specific function
        Sleep(100);
    }
    
    m_pClient->StartOccasionalPing();
    
}

void Multiplayer::checkForPackets()
{
    
    //TODO: Implement handling of the following Rak-Net built in packet types:
	//NOTE: These can all be implemented in level 1 code, as they are fundamental and static
	//ID_DISCONNECTION_NOTIFICATION 		[S\C]The system specified in Packet::playerID has disconnected from us. For the client, this would mean the server has shutdown.
	//ID_CONNECTION_LOST					[S\C]Reliable packets cannot be delivered to the system specifed in Packet::playerID. The connection to that system has been closed. (ungraceful disconnect)
	//ID_CONNECTION_ATTEMPT_FAILED			[S\C]Sent to the player when a connection request cannot be completed due to inability to connect.
	//ID_CONNECTION_REQUEST_ACCEPTED		[C]In a client/server environment, our connection request to the server has been accepted.
    //ID_REMOTE_DISCONNECTION_NOTIFICATION 	[C]In a client/server environment, a client other than ourselves has disconnected gracefully. Packet::playerID is modified to reflect the playerID of this client. 
	//ID_REMOTE_CONNECTION_LOST 			[C]In a client/server environment, a client other than ourselves has been forcefully dropped. Packet::playerID is modified to reflect the playerID of this client. 
	//ID_REMOTE_NEW_INCOMING_CONNECTION 	[C]In a client/server environment, a client other than ourselves has connected. Packet::playerID is modified to reflect the playerID of this client. 
	//ID_REMOTE_EXISTING_CONNECTION 		[C]On our initial connection to the server, we are told of every other client in the game. Packet::playerID is modified to reflect the playerID of this client.
    
    //std::cout << "Start of packet checking" << std::endl;
    
    //What we can do now is hard-code any events that we want to, and otherwise
    // leave it up to events in the map.
    
    //Handle server packets
    if (m_bIsServer) {
        Packet* pPacket = m_pServer->Receive();
        if (pPacket) {
			
            //Create a bitstream from the packet and extract the packet ID
            RakNet::BitStream bsData(pPacket->data, pPacket->length, false);
            unsigned char cTypeID;
            unsigned long iTimeStamp;
            bsData.Read(cTypeID);
            //Check for a timestamp
            if (cTypeID == ID_TIMESTAMP) {
                bsData.Read(iTimeStamp);
                bsData.Read(cTypeID);
            }
            
            //Handle each type of packet
            switch (cTypeID) {
                case ID_NEW_INCOMING_CONNECTION:
                    std::cout << "New incoming connection from player " << pPacket->playerId.ToString() << std::endl;
                    //NOTE: This is a core multiplayer event, so can justify specific handler function
                    onClientConnect(pPacket->playerId);
                    break;
                default: {
					//Calculate the index of the *custom* event
                    int iEventIndex = cTypeID - ID_USER_PACKET_ENUM;
                    //Check that it's not simply an unhandled *core* event
                    if ( iEventIndex < 0 ) {
                    	std::cerr << "Warning in Multiplayer::checkForPackets: \
Unhandled core server packet with ID \""
                                  << (unsigned int)cTypeID << "\"" << std::endl;
					}
					else {
						//Lookup the event with the calculated index
						//NOTE: We are looking-up CLIENT events
						m_pClientResponseEventArray[iEventIndex]->
						                                      respond( bsData );
					}
					/*
					//Now we look in the map of network events
                    ServerEventMapIterator itFoundEvent = 
                                              m_pServerEventMap.find( cTypeID );
                    if ( itFoundEvent != m_pServerEventMap.end() )
                        (*itFoundEvent)->activate();
                    else
                        std::cerr << "Error in Multiplayer::checkForPackets(): \
                                      Unknown\unhandled server packet ID \""
                                  << (unsigned int)cTypeID << "\"" << std::endl;
                    */
                    break; }
            } //switch
        } //if packet
    } //if isServer
    
    //Handle client packets
    else {
        Packet* pPacket = m_pClient->Receive();
        if (pPacket) {
            //Create a bitstream from the packet and extract the packet ID
			RakNet::BitStream bsData(pPacket->data, pPacket->length, false);
            unsigned char cTypeID;
            unsigned long iTimeStamp;
            bsData.Read(cTypeID);
            //Check for a timestamp
            if (cTypeID == ID_TIMESTAMP) {
                bsData.Read(iTimeStamp);
                bsData.Read(cTypeID);
            }
            
            //Handle each type of packet
            switch (cTypeID) {
                case ID_CONNECTION_REQUEST_ACCEPTED:
                    std::cout << "Connection accepted" << std::endl;
                    break;
                //TODO: Implement these
                /*case ID_NETWORKOBJECT_UPDATE:
                    //std::cout << "Update object" << std::endl;
                    updateObjectFromBitStream(bsData);
                    
                    break;
                case ID_CREATE_NETWORKOBJECT:
                    {
                    CCreateObjectEvent event;
                    event.respond(bsData);
                    
                    std::cout << "Create object" << std::endl;
                    //createObjectFromBitStream(bsData);
                    }
                    break;*/
                default: {
					//Calculate the index of the *custom* event
                    int iEventIndex = cTypeID - ID_USER_PACKET_ENUM;
                    //Check that it's not simply an unhandled *core* event
                    if ( iEventIndex < 0 ) {
                    	std::cerr << "Warning in Multiplayer::checkForPackets: \
Unhandled core client packet with ID \""
                                  << (unsigned int)cTypeID << "\"" << std::endl;
					}
					else {
						//Lookup the event with the calculated index
						//NOTE: We are looking-up SERVER events
						m_pServerResponseEventArray[iEventIndex]->
						                                    respond( bsData );
					}
					/*
					//Now we look in the map of network events
                    ServerEventMapIterator itFoundEvent = 
                                              m_pServerEventMap.find( cTypeID );
                    if ( itFoundEvent != m_pServerEventMap.end() )
                        (*itFoundEvent)->activate();
                    else
                        std::cerr << "Error in Multiplayer::checkForPackets(): \
                                      Unknown\unhandled server packet ID \""
                                  << (unsigned int)cTypeID << "\"" << std::endl;
                    */
                    break; }
            } //switch
        } //if packet
    } //if isClient
}
/*
void Multiplayer::updateObjectFromBitStream(RakNet::BitStream& bsData)
{
    
    //Read the network ID of the object
    NetworkID nidObjectID;
    bsData.Read(nidObjectID);
    
    //Get a pointer to the object specified by nidObjectID
    CNetworkObject* pObject = (CNetworkObject*) NetworkIDGenerator::GET_OBJECT_FROM_ID(nidObjectID);
    
    if (!pObject) {
        
        std::cout << "Error - network object with networkID " << nidObjectID.localSystemId << " not found" << std::endl;
        return;
        
    }
    else {
        
        //std::cout << "UnError - network object with networkID " << nidObjectID.localSystemId << " found" << std::endl;
        
    }
    
    //Update the object using the bitstream
    pObject->updateFromBitStream(bsData);

}

//TODO: Think about how to do server acting as client
//      Possibly if (m_bIsServer) { //Do server stuff } //Do client stuff
//      Or use if (m_bIsClient) as well for dedicated option

void Multiplayer::update()
{
    
    //If server then send update packets for all network objects
    if (m_bIsServer) {
        
        for (int i=0;i<m_pObjects.size();++i) {
            
            RakNet::BitStream bsUpdateData;
            
            //bsUpdateData.Write(m_pObjects.GetNetworkID());
            
            m_pObjects[i]->generateUpdateBitStream(bsUpdateData);
            
            //Send the update packet to all connected clients
            m_pServer->Send(&bsUpdateData, MEDIUM_PRIORITY, RELIABLE_SEQUENCED, 0, UNASSIGNED_PLAYER_ID, true);
            
        }
        
    }
    
}
*/
void Multiplayer::disconnect()
{
    
    //TODO: Think about whether m_pServer and m_pClient should be destroyed here or elsewhere
    
    if (m_bIsServer) {
        //m_bIsServer = false;
        m_pServer->Disconnect(300);
        
        // We're done with the network
        RakNetworkFactory::DestroyRakServerInterface(m_pServer);
    }
    else {
        m_pClient->Disconnect(300);
        
        // We're done with the network
        RakNetworkFactory::DestroyRakClientInterface(m_pClient);
    }
    
}

void Multiplayer::onClientConnect(PlayerID pidPlayer)
{
	std::cout << "Multiplayer::onClientConnect" << std::endl;
	/*
	//Send the new client a all of the current network objects
    for (int i=0;i<m_pObjects.size();++i) {
        
        //TODO: Replace this "Ship" hack with string type member in gameobject
        //sendCreateNetworkObjectEvent(m_pObjects[i], "Ship");
        
        //NOTE: We are only calling send here, not activate.
		//CCreateObjectEvent event(m_pObjects[i], "Ship");
        //event.send(pidPlayer);
        
        
    }
    */
}
/*
//TODO: Think about who (can) calls this, and if it should only be the server
CNetworked* Multiplayer::addNetworked(CNetworked* pObject)
{
    
    if( pObject )
        m_pObjects.push_back(pObject);
    
    return pObject;
    
}
*/
/*
bool Multiplayer::registerClientEvent( ClientEvent* pEvent )
{
    //First check that an event with the same packet ID has not already
    //been added.
    ClientEventMapIterator itFoundEvent = 
                          m_pClientEventMap.find( pEvent.getpacketID() );
    if ( itFoundEvent != m_pClientEventMap.end() ) {
        std::cerr << "Error in Multiplayer::addClientEvent: A client \
                      event with packet ID \"" << pEvent->getPacketID()
                      << "\" already exists in the event map. Bailing."
                      << std::endl;
        return false;
    }
    //Then add the new event to the map under its packet ID
    m_pClientEventMap[pEvent->getPacketID()] = pEvent;
    return true;
}

bool Multiplayer::registerServerEvent( ServerEvent* pEvent )
{
    //First check that an event with the same packet ID has not already
    //been added.
    ServerEventMapIterator itFoundEvent = 
                          m_pServerEventMap.find( pEvent.getpacketID() );
    if ( itFoundEvent != m_pServerEventMap.end() ) {
        std::cerr << "Error in Multiplayer::addServerEvent: A server \
                      event with packet ID \"" << pEvent->getPacketID()
                      << "\" already exists in the event map. Bailing."
                      << std::endl;
        return false;
    }
    //Then add the new event to the map under its packet ID
    m_pServerEventMap[pEvent->getPacketID()] = pEvent;
    return true;
}
*/
//---------------------------------------------------------

bool CNetIDGenerator::IsNetworkIDAuthority (void) const
{
    
    return Multiplayer::get()->isServer();
    
}
